package mlp.game.scootaloo;

import java.util.ArrayList;

import com.overkill.game.GameEngine;
import com.overkill.game.GameOptions;
import com.overkill.game.GameOverListener;
import com.overkill.game.Sprite;

import mlp.game.scootaloo.Obstacle.Type;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;

public class ScootalooEngine extends GameEngine implements AccelerometerListener {
	
	public ScootalooEngine(GameOptions options, GameOverListener gameOverListener, int targetFps){
		super(options, gameOverListener, targetFps);
		controllSheme = options.getInteger("controllSheme", CONTROLLSHEME_SCREEN);
		onScreenHelpAlpha = options.getInteger("helpAlpha", 255);
	}

	public static final int CONTROLLSHEME_DPAD = 0;
	public static final int CONTROLLSHEME_SCREEN = 1;
	public static final int CONTROLLSHEME_ACCELEROMETER = 2;
	
	public static final int KeyEvent_KEYCODE_BUTTON_START = 108; // Added on API 9
	
	public static final String TYPEFACE_PATH = "fonts/celestiaredux.ttf";
	public static final boolean SHOW_DEBUG_INFO = false;
	
	public static float GAME_SPEED = 1;
	private static float MAX_GAME_SPEED = 0;
	private static float GAME_SPEED_MODIFIER = 0;
	
	public static float scoreDistance = 0;
	private float scoreNegativ = 0;
	private int scoreCupcakes = 0;

	private int ground = 0;
	private Background background;
	private Scootaloo player;
	private ArrayList<Obstacle> obstacles = new ArrayList<Obstacle>();
	private ObstacleSpawner spawner;
	
	Paint scoreText = new Paint();
	Paint scoreBoardFill = new Paint();
	Paint scoreBoardStroke = new Paint();
	Paint pauseTextBig = new Paint();
	Paint pauseTextSmall = new Paint();
	
	Paint onScreenInfo = new Paint();

	int backgroundColor = 0xFFEDA887;
	int countdownLeft = 0;
	
	private GameOverListener gameOverListener;

	long gameStartTime;
	long gameRunTime;
	long gamePauseTime;

	private int controllSheme = CONTROLLSHEME_SCREEN;
	private int onScreenHelpAlpha = 255;
	private Path pLeft;
	private Path pCenter;
	private Path pRight;
	private Bitmap onScreenControll;
		
	@Override
	public void inizialize(Rect stage) {
		super.inizialize(stage);
		
		GAME_SPEED = dip(6.5f);
		MAX_GAME_SPEED = dip(20);
		GAME_SPEED_MODIFIER = dip(0.0015f);
		
		gamePaused = false;
		scoreDistance = 0;
		scoreCupcakes = 0;
		
		Sprite.debugPaint = new Paint();
		Sprite.debugPaint.setColor(Color.BLACK);
		Sprite.debugPaint.setStyle(Style.STROKE);
				
		ground = (int) (stage.bottom - dip(55));
		
		background = new Background(new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.background), 30, 1));
		
		Paint shadowPaint = new Paint();
		shadowPaint.setColor(Color.BLACK);
		shadowPaint.setStyle(Style.FILL);
		shadowPaint.setAntiAlias(true);
		shadowPaint.setAlpha(38);
		
		player = new Scootaloo(
						new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.scootaloo), 30, 8),
						new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.scootaloo_wings), 30, 3),
						ground,
						shadowPaint,
						getContext().getResources().getDimension(R.dimen.maxJumpVelocity));
		player.setWingsOffset(dip(33), dip(28));
		Log.i("initialize", "Scootaloo.height:" + player.getHeight());
		
		spawner = new ObstacleSpawner(new Sprite[] {
				new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.cupcake_1), 30, 1),
				new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.cupcake_2), 30, 1),
				new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.cupcake_3), 30, 1)
			},
			new Sprite[] {new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.obstacle_1), 30, 1)},
			new Sprite[] {new Sprite(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.derpy), 30, 7)},
						ground,
						shadowPaint);

		obstacles = new ArrayList<Obstacle>();
		
		Typeface celestia = Typeface.createFromAsset(getContext().getAssets(), TYPEFACE_PATH);
		
		if (controllSheme == CONTROLLSHEME_SCREEN){	
			// init screen areas for onscreen controlls
			pLeft = new Path();
			pLeft.moveTo(stage.left, stage.top);
			pLeft.lineTo(stage.centerX(), stage.bottom - dip(50));
			pLeft.lineTo(stage.left, stage.bottom - dip(50));
			pLeft.close();
			
			pCenter = new Path();
			pCenter.moveTo(stage.left, stage.top);
			pCenter.lineTo(stage.centerX(), stage.bottom - dip(50));
			pCenter.lineTo(stage.right, stage.top);
			pCenter.close();
			
			pRight = new Path();
			pRight.moveTo(stage.right, stage.top);
			pRight.lineTo(stage.right, stage.bottom - dip(50));
			pRight.lineTo(stage.centerX(), stage.bottom - dip(50));
			pRight.close();				
			
			onScreenInfo = new Paint();
			onScreenInfo.setStrokeWidth(dip(2f));
			onScreenInfo.setTextSize(dip(22));
			onScreenInfo.setStyle(Style.STROKE);
			onScreenInfo.setColor(Color.BLACK);
			onScreenInfo.setTypeface(celestia);
			
			onScreenControll = Bitmap.createBitmap(stage.width(), stage.height(), Bitmap.Config.ARGB_8888);
			Path paths[] = new Path[]{pLeft, pCenter, pRight};
			//do this so that regions outside any path have a default
			//path index of 255
			onScreenControll.eraseColor(0x00000000);

			Canvas canvas = new Canvas(onScreenControll);
			Paint paint = new Paint();

			//these are defaults, you only need them if reusing a Paint
			paint.setAntiAlias(false);
			paint.setStyle(Paint.Style.FILL);
			
			for(int i=0; i<paths.length; i++) {
				paint.setColor(Color.argb(i + 1, 0, 0, 0)); // use only alpha value for color 0xXX000000
				canvas.drawPath(paths[i], paint); 
			}
		}else if(controllSheme == CONTROLLSHEME_ACCELEROMETER){
			AccelerometerManager.startListening(getContext(), this);
		}
		
		// init score paint
		scoreText.setColor(Color.BLACK);
		scoreText.setTextAlign(Align.CENTER);
		scoreText.setTextSize(dip(18));
		scoreText.setTypeface(celestia);
		scoreText.setAntiAlias(true);

		pauseTextBig = new Paint(scoreText);
		pauseTextBig.setTextSize(dip(40));
		pauseTextSmall = new Paint(scoreText);
		pauseTextSmall.setTextSize(dip(18));
		
		
		scoreBoardFill.setColor(0xCCFFFFFF);
		scoreBoardFill.setStyle(Style.FILL);
		scoreBoardStroke.setColor(0xCCAB4388);
		scoreBoardStroke.setStyle(Style.STROKE);
		scoreBoardStroke.setStrokeWidth(dip(3.3f));

		// Set game end time
		gameStartTime = SystemClock.elapsedRealtime() + countdownLeft * 1000;
	}
	
	@Override
	public void gameOver() {
		if(controllSheme == CONTROLLSHEME_ACCELEROMETER)
			AccelerometerManager.stopListening();
		GameOptions options = new GameOptions();
		options.put("scoreDistance", scoreDistance);
		options.put("scoreCupcakes", scoreCupcakes);
		super.gameOver(options);
	}
	
	@Override
	public void update(long gameTime, int deltaTime) {
		gameRunTime = (gameTime - gameStartTime) / 1000;
		if (gamePaused == true)
			return;
		if (gameTime >= gameStartTime) {
			gameRunning = true;
		} else {
			countdownLeft = (int) ((gameStartTime - gameTime) / 1000);
		}
		if (gameRunning == false)
			return;		
		
		if(GAME_SPEED < MAX_GAME_SPEED){
			GAME_SPEED += GAME_SPEED_MODIFIER;
		}
		
		if(controllSheme == CONTROLLSHEME_SCREEN){
			int a = onScreenInfo.getAlpha();
			a -= 1;
			if(a < onScreenHelpAlpha)
				a = onScreenHelpAlpha;
			onScreenInfo.setAlpha(a);
		}
		
		scoreDistance += GAME_SPEED;

		background.update(gameTime);
		player.update(gameTime);
		
		spawner.spawn(obstacles);
		updateObstacles(gameTime);
		updateScore();
	}

	private void updateObstacles(long gameTime){
		ArrayList<Obstacle> obstaclesToRemove = new ArrayList<Obstacle>();
		for (Obstacle o : obstacles) {
			o.update(gameTime);
			if(o.getFrame().right < stage.left){
				obstaclesToRemove.add(o);
			} else {
				if(o.getType() == Type.OBSTACLE && player.isOnTop(o) && player.jumpVelocity > 0){
					player.onTop = o;
					if(player.upKey == false)
						player.resetJump();
				}
				if(player.onTop != null && player.isOnTop(player.onTop) == false){
					player.onTop = null;
					if(player.upKey == false)
						player.jumpRelease = false;
				}
				if(o.isHit(player) && player.onTop == null){
					if(o.getType() == Type.CUPCAKE){
						scoreCupcakes += 1;
						player.collect(o);
						obstaclesToRemove.add(o);
					} else {
						this.gameOver();
					}
				}
			}
		}	
		obstacles.removeAll(obstaclesToRemove);
	}
	
	private void updateScore(){
		if(player.velocity < 0 || scoreNegativ < 0){
			scoreNegativ += player.velocity;
		} else {
			scoreDistance += scoreNegativ;
			scoreNegativ = 0;
			scoreDistance += player.velocity;
		}
		scoreDistance += GAME_SPEED;
	}
		
	@Override
	public void draw(Canvas canvas) {
		// Draw Background
		drawBackground(canvas);
		
		for (Obstacle o : obstacles) {
			o.draw(canvas);
		}
		player.draw(canvas);
		// Draw score
		this.drawScore(canvas);		
		// Draw Pause screen
		if (gamePaused == true) {
			this.drawPauseScreen(canvas);
		}
		
		if(controllSheme == CONTROLLSHEME_SCREEN && onScreenInfo.getAlpha() > 0){
			onScreenInfo.setStyle(Style.STROKE);
			canvas.drawPath(pLeft, onScreenInfo);
			canvas.drawPath(pCenter, onScreenInfo);
			canvas.drawPath(pRight, onScreenInfo);
			onScreenInfo.setStyle(Style.FILL);
			onScreenInfo.setTextAlign(Align.LEFT);
			canvas.drawText("<<< Move Left", dip(5), stage.centerY(), onScreenInfo);
			onScreenInfo.setTextAlign(Align.CENTER);
			canvas.drawText("^^^ Jump ^^^", stage.centerX(), stage.centerY() - dip(30), onScreenInfo);
			onScreenInfo.setTextAlign(Align.RIGHT);
			canvas.drawText("Move Right >>>", stage.right - dip(5), stage.centerY(), onScreenInfo);
		}	
	}

	private void drawPauseScreen(Canvas canvas){
		int y = (int) (stage.height() / 2 - pauseTextBig.getTextSize() / 2);
		canvas.drawText(getString(R.string.pause_pause),
				stage.width() / 2, y, pauseTextBig);
		canvas.drawText(getString(R.string.pause_tabanywhere),
				stage.width() / 2, (float) (y + pauseTextBig.getTextSize() / 2), pauseTextSmall);
	}
	
	private void drawBackground(Canvas canvas) {
		background.draw(canvas);
//		canvas.drawColor(backgroundColor);
	}

	private void drawScore(Canvas canvas) {
		RectF scoreRectF = new RectF(0, -25, stage.right, (float) (scoreText.getTextSize() * 1.4));
		canvas.drawRoundRect(scoreRectF, 20, 20, scoreBoardFill);
		canvas.drawRoundRect(scoreRectF, 20, 20, scoreBoardStroke);
		scoreText.setTextAlign(Align.LEFT);
		canvas.drawText("Cupcakes: " + scoreCupcakes,
							stage.left + dip(10),
							scoreText.getTextSize(), scoreText);
		scoreText.setTextAlign(Align.RIGHT);
		canvas.drawText("Lifes: 1",
							stage.right - dip(10),
							scoreText.getTextSize(), scoreText);
		scoreText.setTextAlign(Align.CENTER);
		canvas.drawText("Score: " + Math.round(scoreDistance),
							stage.centerX(),
							scoreText.getTextSize(), scoreText);
	}

	@Override
	public void handleTouchEvent(int action, float x, float y){		
		if(action == MotionEvent.ACTION_DOWN && this.gamePaused){
			this.resumeGame();
		}	
		int keyCode = 0;
		if(controllSheme == CONTROLLSHEME_ACCELEROMETER){
			keyCode = KeyEvent.KEYCODE_DPAD_CENTER;
			
		} else if (controllSheme == CONTROLLSHEME_SCREEN){				
			int pathIndex = Color.alpha(onScreenControll.getPixel((int)x, (int)y)) - 1;					
			switch (pathIndex) {
				case 0:
					keyCode = KeyEvent.KEYCODE_DPAD_LEFT;				
					break;
				case 1:
					keyCode = KeyEvent.KEYCODE_DPAD_CENTER;				
					break;
				case 2:
					keyCode = KeyEvent.KEYCODE_DPAD_RIGHT;				
					break;
				default:
					break;
			}
			
		} else if (controllSheme == CONTROLLSHEME_DPAD){
			if(x < stage.centerX()){
				keyCode = KeyEvent.KEYCODE_DPAD_LEFT;
			}
			if(x >= stage.centerX()){
				keyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
			}	
		}
		if(action == KeyEvent.ACTION_DOWN){
			this.onKeyDown(keyCode, new KeyEvent(action, keyCode));
		} else if(action == KeyEvent.ACTION_UP){
			this.onKeyUp(keyCode, new KeyEvent(action, keyCode));
		}
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_LEFT:
				player.leftKey = true;
				player.rightKey = false;
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				player.rightKey = true;		
				player.leftKey = false;		
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
				player.upKey = true;
				player.hover = true;
				break;
			case KeyEvent.KEYCODE_MENU:
				player.upKey = true;
				player.hover = true;
				break;				
			case KeyEvent_KEYCODE_BUTTON_START:
				if(this.gamePaused){
					this.resumeGame();
				} else {
					this.pauseGame();
				}
			default:
				return false;
		}
		return true;
	}
	
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_LEFT:
				player.leftKey = false;
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				player.rightKey = false;				
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
				player.upKey = false;
				player.hover = false;
				player.jumpRelease = false;
			case KeyEvent.KEYCODE_MENU:
				player.upKey = false;
				player.hover = false;
				player.jumpRelease = false;
				break;
			default:
				return false;
		}
		return true;
	}
	
	public void setGameOverListener(GameOverListener gameOverListener) {
		this.gameOverListener = gameOverListener;
	}

	public GameOverListener getGameOverListener() {
		return gameOverListener;
	}

	
	/**
	 * Returns a pixel value for the given dip (Density Independent Pixel) value
	 * @param value the dip value to convert
	 * @return dip * scale
	 */
	public static float dip(float value){
		return SCALE * value;
	}

	@Override
	public void onAccelerationChanged(float x, float y, float z) {
		Log.i("onAccelerationChanged", "x:" + x + " y:" + y + " z:" + z);		
	}

	public int getControllSheme() {
		return controllSheme;
	}

	public void setControllSheme(int controllSheme) {
		this.controllSheme = controllSheme;
	}

	public void setOnScreenHelpAlpha(int onScreenHelpAlpha) {
		this.onScreenHelpAlpha = (int) ( 255f * (onScreenHelpAlpha / 100f));
		Log.i("RenderView", "onScreenHelpAlpha:" + onScreenHelpAlpha);
	}

	public int getOnScreenHelpAlpha() {
		return onScreenHelpAlpha;
	}

	@Override
	public void onShake(float force) {	}

	@Override
	public void onTilt(TiltDirection direction) {
		switch (direction) {
		case NONE:
			player.leftKey = false;
			player.rightKey = false;
			break;
		case LEFT:
			player.leftKey = true;
			player.rightKey = false;
			break;
		case RIGHT:
			player.leftKey = false;
			player.rightKey = true;
			break;
		default:
			break;
		}
	}
}
